#include "stdafx.h"
#include "filesys/directoryiterator.hpp"

namespace JHCPP
{
	namespace filesys
	{
	#if defined(JH_OS_WINDOWS)
		CDirectoryIteratorImpl::CDirectoryIteratorImpl(const std::string& path) : m_fh(INVALID_HANDLE_VALUE), m_rc(1)
		{
			CPath p(path);
			p.makeDirectory();
			std::string findPath = p.toString();
			findPath.append("*");

			m_fh = ::FindFirstFile(findPath.c_str(), &m_fd);
			if (m_fh == INVALID_HANDLE_VALUE)
			{
				if (GetLastError() != ERROR_NO_MORE_FILES)
					CFile::handleLastError(path);
			}
			else
			{
				m_current = m_fd.cFileName;
				if (m_current == "." || m_current == "..")	
					next();
			}
		}

		CDirectoryIteratorImpl::~CDirectoryIteratorImpl()
		{
			if (m_fh != INVALID_HANDLE_VALUE)
				::FindClose(m_fh);
		}

		void CDirectoryIteratorImpl::add()
		{
			++m_rc;
		}

		void CDirectoryIteratorImpl::release()
		{
			if (--m_rc == 0)
				delete this;
		}

		const std::string& CDirectoryIteratorImpl::get() const
		{
			return m_current;
		}

		const std::string& CDirectoryIteratorImpl::next()
		{
			do
			{
				if (::FindNextFile(m_fh, &m_fd) != 0)
					m_current = m_fd.cFileName;
				else
					m_current.clear();
			}
			while (m_current == "." || m_current == "..");
			return m_current;
		}
	#elif define(JH_OS_LINUX)
		CDirectoryIteratorImpl::CDirectoryIteratorImpl(const std::string& path) : m_pDir(0), m_rc(1)
		{
			CPath p(path);
			p.makeFile();

			m_pDir = opendir(p.toString().c_str());

			if (!m_pDir) CFile::handleLastError(path);

			next();
		}

		CDirectoryIteratorImpl::~CDirectoryIteratorImpl()
		{
			if (m_pDir) 
				closedir(m_pDir);
		}

		void DirectoryIteratorImpl::add()
		{
			++m_rc;
		}

		void DirectoryIteratorImpl::release()
		{
			if (--m_rc == 0)
				delete this;
		}

		const std::string& CDirectoryIteratorImpl::get() const
		{
			return m_current;
		}

		const std::string& CDirectoryIteratorImpl::next()
		{
			do
			{
				struct dirent* pEntry = readdir(m_pDir);
				if (pEntry)
					m_current = pEntry->d_name;
				else
					m_current.clear();
			}
			while (m_current == "." || m_current == "..");
			return m_current;
		}
	#endif 


		CDirectoryIterator::CDirectoryIterator(): m_pImpl(0)
		{
		}

		CDirectoryIterator::CDirectoryIterator(const std::string& path): m_path(path), m_pImpl(new CDirectoryIteratorImpl(path))
		{
			m_path.makeDirectory();
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
		}

		CDirectoryIterator::CDirectoryIterator(const CDirectoryIterator& iterator): m_path(iterator.m_path), m_pImpl(iterator.m_pImpl)
		{
			if (m_pImpl) 
			{
				m_pImpl->add();
				m_file = m_path;
			}
		}

		CDirectoryIterator::CDirectoryIterator(const CFile& file): m_path(file.path()), m_pImpl(new CDirectoryIteratorImpl(file.path()))
		{
			m_path.makeDirectory();
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
		}

		CDirectoryIterator::CDirectoryIterator(const CPath& path): m_path(path), m_pImpl(new CDirectoryIteratorImpl(path.toString()))
		{
			m_path.makeDirectory();
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
		}

		CDirectoryIterator::~CDirectoryIterator()
		{
			if (m_pImpl)
				m_pImpl->release();
		}

		const std::string& CDirectoryIterator::name() const
		{
			return m_path.getFileName();
		}

		const CPath& CDirectoryIterator::path() const
		{
			return m_path;
		}

		CDirectoryIterator& CDirectoryIterator::operator = (const CDirectoryIterator& it)
		{
			if (m_pImpl) 
				m_pImpl->release();

			m_pImpl = it.m_pImpl;

			if (m_pImpl) 
			{
				m_pImpl->add();
				m_path = it.m_path;
				m_file = m_path;
			}
			return *this;
		}

		CDirectoryIterator& CDirectoryIterator::operator = (const CFile& file)
		{
			if (m_pImpl)
				m_pImpl->release();

			m_pImpl = new CDirectoryIteratorImpl(file.path());
			m_path.parseDirectory(file.path());
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
			return *this;
		}

		CDirectoryIterator& CDirectoryIterator::operator = (const CPath& path)
		{
			if (m_pImpl) 
				m_pImpl->release();

			m_pImpl = new CDirectoryIteratorImpl(path.toString());
			m_path = path;
			m_path.makeDirectory();
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
			return *this;
		}

		CDirectoryIterator& CDirectoryIterator::operator = (const std::string& path)
		{
			if (m_pImpl) 
				m_pImpl->release();
			m_pImpl = new CDirectoryIteratorImpl(path);
			m_path.parseDirectory(path);
			m_path.setFileName(m_pImpl->get());
			m_file = m_path;
			return *this;
		}

		CDirectoryIterator& CDirectoryIterator::operator ++ ()
		{
			if (m_pImpl)
			{
				m_path.setFileName(m_pImpl->next());
				m_file = m_path;
			}
			return *this;
		}

		CDirectoryIterator CDirectoryIterator::operator ++ (int dummy)
		{
			if (m_pImpl)
			{
				m_path.setFileName(m_pImpl->next());
				m_file = m_path;
			}
			return *this;
		}

		const CFile& CDirectoryIterator::operator * () const
		{
			return m_file;
		}

		CFile& CDirectoryIterator::operator * ()
		{
			return m_file;
		}

		const CFile* CDirectoryIterator::operator -> () const
		{
			return &m_file;
		}

		CFile* CDirectoryIterator::operator -> ()
		{
			return &m_file;
		}

		bool CDirectoryIterator::operator == (const CDirectoryIterator& iterator) const
		{
			return name() == iterator.name();
		}

		bool CDirectoryIterator::operator != (const CDirectoryIterator& iterator) const
		{
			return name() != iterator.name();
		}
	}//end of namespace filesys
}//end of namespace JHCPP